package qc.module.duty.service;


import com.alibaba.excel.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import qc.common.core.exception.QCPromptException;
import qc.common.core.unify.QCUnifyReturnValue;
import qc.common.core.utils.DateUtil;
import qc.module.duty.dto.group.DutyGroupDto;
import qc.module.duty.dto.schedule.*;
import qc.module.duty.dto.staff.DutyStaffDto;
import qc.module.duty.dto.team.DutyTeamMemberDto;
import qc.module.duty.entity.DutySchedule;
import qc.module.duty.entity.DutyScheduleMember;
import qc.module.duty.mapper.DutyScheduleMapper;
import qc.module.duty.mapper.DutyScheduleMemberMapper;
import qc.module.duty.repository.DutyScheduleRepository;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class DutyScheduleService {
    private DutyScheduleRepository dutyScheduleRepository;

    @Autowired
    public void setDutyScheduleRepository(DutyScheduleRepository dutyScheduleRepository) {
        this.dutyScheduleRepository = dutyScheduleRepository;
    }

    private DutyTeamMemberService dutyTeamMemberService;

    @Autowired
    public void setDutyTeamMemberService(DutyTeamMemberService dutyTeamMemberService) {
        this.dutyTeamMemberService = dutyTeamMemberService;
    }

    private DutyScheduleMemberService dutyScheduleMemberService;

    @Autowired
    public void setDutyScheduleMemberService(DutyScheduleMemberService dutyScheduleMemberService) {
        this.dutyScheduleMemberService = dutyScheduleMemberService;
    }

    private DutyStaffService dutyStaffService;

    @Autowired
    public void setDutyStaffService(DutyStaffService dutyStaffService) {
        this.dutyStaffService = dutyStaffService;
    }

    private DutyGroupService dutyGroupService;

    @Autowired
    public void setDutyGroupService(DutyGroupService dutyGroupService) {
        this.dutyGroupService = dutyGroupService;
    }

    /***
     * 查询指定时间段和值班分组ID集合的排班信息
     *
     * @param beginTime 起始时间
     * @param endTime 截止时间
     * @param filterGroupIds 值班分组ID集合
     * @return java.util.List<qc.module.duty.entity.DutySchedule>
     * @author QuCheng Tech
     * @since 2023/8/12
     */
    public List<DutySchedule> querySchedules(Date beginTime, Date endTime, List<Integer> filterGroupIds) {
        LambdaQueryWrapper<DutySchedule> wrapper = new LambdaQueryWrapper<>();
        //查询条件：DB中的开始时间<=endTime && DB中的截止时间>=beginTime
        wrapper.le(DutySchedule::getBegintm, endTime);
        wrapper.ge(DutySchedule::getEndtm, beginTime);
        wrapper.in(DutySchedule::getGid, filterGroupIds);

        wrapper.orderByAsc(DutySchedule::getBegintm);
        wrapper.orderByAsc(DutySchedule::getGid);

        return dutyScheduleRepository.selectList(wrapper);
    }

    /**
     * 设置班次
     *
     * @param condition:设置信息
     * @return String 成功返回null。失败返回错误信息
     * @author QcCheng Tech
     * @date 2023/8/12
     */
    public String setShift(DutyScheduleShiftSetDto condition) throws QCPromptException {
        if (condition == null)
            return QCUnifyReturnValue.Warn("设置对象不能为空");
        if (condition.getId() < 0x1)
            return QCUnifyReturnValue.Warn("排班ID不能为空");
        if (StringUtils.isBlank(condition.getBegintm()))
            return QCUnifyReturnValue.Warn("值班开始时间为空");
        if (StringUtils.isBlank(condition.getEndtm()))
            return QCUnifyReturnValue.Warn("值班结束时间不能为空");
        if (!hasIdExist(condition.getId())) {
            return QCUnifyReturnValue.Warn("修改的排班信息不存在");
        }
        Date BeginTime = null;
        Date endTime = null;
        try {
            // 解析开始时间
            BeginTime = DateUtil.parseDate(condition.getBegintm());
            condition.setBegintm(DateUtil.getDateTimeString(BeginTime));
        } catch (Exception e) {
            // 抛出自定义异常，开始时间转换错误
            throw new QCPromptException("开始时间转换错误");
        }

        try {
            // 解析结束时间
            endTime = DateUtil.parseDate(condition.getEndtm());
            condition.setEndtm(DateUtil.getDateTimeString(endTime));
        } catch (Exception e) {
            // 抛出自定义异常，截止时间转换错误
            throw new QCPromptException("截止时间转换错误");
        }
        DutySchedule dutySchedule = DutyScheduleMapper.MAPPER.toEntity(condition);
        dutySchedule.setIntm(DateUtil.getNowDate());
        if (dutyScheduleRepository.updateById(dutySchedule) < 1) {
            return QCUnifyReturnValue.Warn("修改失败");
        }
        return null;
    }

    /**
     * 自动根据间隔的天数生成排班班次
     *
     * @param condition: 设置信息
     * @return String 成功返回mull ，失败返回错误信息
     * @author QcCheng Tech
     * @date 2023/8/12
     */
    public String batchSetShift(DutyScheduleBatchSetDto condition) throws QCPromptException {
        // 检查设置对象是否为空
        if (condition == null) {
            return QCUnifyReturnValue.Warn("设置对象不能为空");
        }

        // 检查值班分组ID集合是否为空
        int[] groupids = condition.getGroupids();
        if (groupids == null || groupids.length == 0) {
            return QCUnifyReturnValue.Warn("值班分组ID集合不能为空");
        }

        // 检查值班开始时间是否为空
        if (StringUtils.isBlank(condition.getBegintm())) {
            return QCUnifyReturnValue.Warn("值班开始时间为空");
        }

        // 检查值班结束时间是否为空
        if (StringUtils.isBlank(condition.getEndtm())) {
            return QCUnifyReturnValue.Warn("值班结束时间不能为空");
        }

        // 检查间隔天数是否小于1
        int days = condition.getDays();
        if (days < 1) {
            return QCUnifyReturnValue.Warn("间隔天数不能为空");
        }

        Date conditionBeginTime;
        Date conditionEndTime;

        try {
            // 解析值班开始时间
            Date date = DateUtil.parseDate(condition.getBegintm());
            //时间精确到日
            conditionBeginTime = DateUtil.parseDate(DateUtil.getDateString(date));
        } catch (Exception e) {
            // 抛出自定义异常，开始时间转换错误
            throw new QCPromptException("开始时间转换错误");
        }

        try {
            // 解析值班结束时间
            Date date = DateUtil.parseDate(condition.getEndtm());
            //时间精确到日
            conditionEndTime = DateUtil.parseDate(DateUtil.getDateString(date));
        } catch (Exception e) {
            // 抛出自定义异常，截止时间转换错误
            throw new QCPromptException("截止时间转换错误");
        }


        if (conditionBeginTime.after(conditionEndTime))
            throw new QCPromptException("起始时间不能大于截止时间");

        for (int groupId : groupids) {
            //先根据分组判断批量设置的起止时间段类的排班记录，如果有将已有排班和排班对应的值班人员均删除
            //判断条件： gid=groupId && begintm>=conditionBeginTime && endtm<=conditionEndTime
            LambdaQueryWrapper<DutySchedule> wrapper = new LambdaQueryWrapper<>();
            //只需要返回班次ID，其他信息不查询
            wrapper.select(DutySchedule::getId);

            wrapper.eq(DutySchedule::getGid, groupId);
            wrapper.ge(DutySchedule::getBegintm, conditionBeginTime);
            wrapper.le(DutySchedule::getEndtm, conditionEndTime);
            List<DutySchedule> existSchedules = dutyScheduleRepository.selectList(wrapper);
            if (existSchedules != null && existSchedules.size() > 0x0) {
                for (DutySchedule schedule : existSchedules) {
                    deleteShift(schedule.getId());
                }
            }

            //一个班次的值班开始时间
            Date shiftBeginTime = conditionBeginTime;
            while (shiftBeginTime.before(conditionEndTime)) {
                //一个班次的值班结束时间，按日计算需往前1日
                Date shiftEndTime = DateUtil.addDays(shiftBeginTime, days - 1);

                // 确保生成的时间段不会超过指定的结束时间
                if (shiftEndTime.after(conditionEndTime)) {
                    shiftEndTime = conditionEndTime;
                }

                // 添加值班排班信息
                DutySchedule dutySchedule = new DutySchedule();
                dutySchedule.setId(genereateId());
                dutySchedule.setGid(groupId);
                dutySchedule.setBegintm(shiftBeginTime);
                dutySchedule.setEndtm(shiftEndTime);
                dutySchedule.setHoliday(condition.isHoliday());
                dutySchedule.setTmtext(condition.getTmtext());
                dutySchedule.setIntm(DateUtil.getNowDate());
                dutyScheduleRepository.insert(dutySchedule);

                //下一个班次的起始时间，天数累加
                shiftBeginTime = DateUtil.addDays(shiftBeginTime, days);
            }
        }

        return QCUnifyReturnValue.Success();
    }

    /**
     * 设置班次人员
     *
     * @param membersDto: 设置班次人员
     * @return String 成功返回null，失败返回错误信息
     * @author QcCheng Tech
     * @date 2023/8/12
     */
    public String setShiftMembers(DutyScheduleSetMembersDto membersDto) {
        if (membersDto == null) {
            return QCUnifyReturnValue.Warn("传入条件不能为空");
        }

        if ((membersDto.getStaffids() == null || membersDto.getStaffids().length == 0) && membersDto.getTeamid() < 1) {
            return QCUnifyReturnValue.Warn("未指定班次成员");
        }

        // 先删除该班次人员再进行添加
        dutyScheduleMemberService.deleteScheduleMembers(membersDto.getId());

        int teamid = membersDto.getTeamid();
        List<Integer> memberIdList = new ArrayList<>();

        // 如果选择了班值就获取该班值中的所有成员
        if (teamid > 0) {
            List<DutyTeamMemberDto> dutyTeamMemberDtos = dutyTeamMemberService.get(teamid);
            memberIdList = dutyTeamMemberDtos.stream()
                    .map(DutyTeamMemberDto::getMemberid)
                    .collect(Collectors.toList());
        }

        int[] staffids = membersDto.getStaffids();
        List<Integer> staffIdList = (staffids != null) ? Arrays.stream(staffids).boxed().collect(Collectors.toList()) : new ArrayList<>();

        List<Integer> mergedList = new ArrayList<>(staffIdList);
        mergedList.addAll(memberIdList);

        // 去除重复人员
        List<Integer> distinctMergedList = mergedList.stream().distinct().collect(Collectors.toList());

        if (distinctMergedList == null || distinctMergedList.isEmpty()) {
            return QCUnifyReturnValue.Warn("选择成员为空");
        }

        // 添加班次人员
        dutyScheduleMemberService.addScheduleMembers(distinctMergedList, membersDto.getId());

        return QCUnifyReturnValue.Success();
    }

    /**
     * 判断指定的ID是否存在
     *
     * @param id 值班排版表id
     * @return true表示已存在，false表示不存在
     * @author QuCheng Tech
     */
    public boolean hasIdExist(Integer id) {
        LambdaQueryWrapper<DutySchedule> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DutySchedule::getId, id);

        DutySchedule en = dutyScheduleRepository.selectOne(wrapper);
        if (en != null)
            return true;

        return false;
    }

    /**
     * 删除指定班次，同时删除班次的值班人员信息
     *
     * @param shiftId: 班次id
     * @return String 成功返回null。失败返回错误信息
     * @author QcCheng Tech
     * @date 2023/8/12
     */
    public String deleteShift(int shiftId) {
        dutyScheduleMemberService.deleteScheduleMembers(shiftId);

        dutyScheduleRepository.deleteById(shiftId);

        return QCUnifyReturnValue.Success();
    }

    /**
     * 生成记录ID，获取数据库表中的最大记录ID+1
     *
     * @return 生成记录ID
     * @author QuCheng Tech
     * @since 2023/5/25
     */
    Integer genereateId() {
        Integer maxRecordId = getMaxId();
        return maxRecordId + 1;
    }

    /**
     * 获取数据库表中的最大ID值，没有记录时返回0
     *
     * @return 返回数据库表中的最大ID值
     * @author QuCheng Tech
     * @since 2023/5/25
     */
    Integer getMaxId() {
        LambdaQueryWrapper<DutySchedule> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(DutySchedule::getId);
        wrapper.orderByDesc(DutySchedule::getId);
        DutySchedule en = dutyScheduleRepository.selectOne(wrapper);
        if (en != null)
            return en.getId();

        return 0x0;
    }

    /**
     * 查询排班信息
     *
     * @param condition: 查询条件
     * @return List<DutyGroupDto> 返回结果集
     * @author QcCheng Tech
     * @date 2023/8/11
     */
    public List<DutyScheduleInfoDto> query(DutyScheduleQueryConditionDto condition, Map<Integer, String> deptIdAndNames) throws QCPromptException {
        if (condition == null) {
            throw new QCPromptException("查询条件不能为空");
        }

        // 检查值班开始时间是否为空
        if (StringUtils.isBlank(condition.getBegintm())) {
            throw new QCPromptException("开始时间不能为空");
        }

        // 检查值班结束时间是否为空
        if (StringUtils.isBlank(condition.getEndtm())) {
            throw new QCPromptException("结束不能为空");
        }
        if ((condition.getGroupids() == null || condition.getGroupids().length == 0)) {
            throw new QCPromptException("值班分组ID集合不能为空");
        }


        Date beginTime = null;
        Date endTime = null;

        if (StringUtils.isNotBlank(condition.getBegintm())) {
            try {
                beginTime = DateUtil.parseDate(condition.getBegintm());
                //时间精确到日
                beginTime = DateUtil.parseDate(DateUtil.getDateString(beginTime));
            } catch (Exception e) {
                throw new QCPromptException("开始时间转换错误");
            }
        }

        if (StringUtils.isNotBlank(condition.getEndtm())) {
            try {
                endTime = DateUtil.parseDate(condition.getEndtm());
                //时间精确到日
                endTime = DateUtil.parseDate(DateUtil.getDateString(endTime));
            } catch (Exception e) {
                throw new QCPromptException("截止时间转换错误");
            }
        }

        if (beginTime.after(endTime))
            throw new QCPromptException("查询排班信息的起始时间不能大于截止时间");

        //根据班组id，集合查询相应的班组
        List<Integer> groupids = Arrays.stream(condition.getGroupids()).boxed().collect(Collectors.toList());
        List<DutyGroupDto> dutyGroupDtos = dutyGroupService.queryGroupDtoList(groupids, deptIdAndNames);


        if (dutyGroupDtos != null && !dutyGroupDtos.isEmpty()) {
            //获取值班分组id集合
            List<Integer> filterGroupIds = dutyGroupDtos.stream()
                    .map(DutyGroupDto::getId)
                    .collect(Collectors.toList());

            //获得值班组名称与对应值班组id集合
            Map<Integer, String> groupIdAndNames = new HashMap<>();
            for (DutyGroupDto dutyGroupDto : dutyGroupDtos) {
                groupIdAndNames.put(dutyGroupDto.getId(), dutyGroupDto.getName());
            }

            //根据起止时间和值班分组信息查询排班表记录
            List<DutySchedule> schedules = querySchedules(beginTime, endTime, filterGroupIds);

            List<DutyScheduleInfoDto> dutyScheduleInfoDtoList = DutyScheduleMapper.MAPPER.toScheduleInfoDtoList(schedules);


            //如果有排班记录，查询出所有的值班人员信息和值班排班人员表中的信息
            List<DutyStaffDto> allDutyStaffs = null;
            List<DutyScheduleShiftMemberDto> dutyScheduleShiftMemberDtos = null;

            if (schedules != null && !schedules.isEmpty()) {
                allDutyStaffs = dutyStaffService.getAllStaffs();
                List<DutyScheduleMember> allScheduleMembers = dutyScheduleMemberService.getAllScheduleMembers();
                dutyScheduleShiftMemberDtos = DutyScheduleMemberMapper.MAPPER.toTeamMemberDtoList(allScheduleMembers);
            }

            //将值班排班人员表中的信息添加到结果集中
            if (dutyScheduleInfoDtoList != null && !dutyScheduleInfoDtoList.isEmpty()) {
                for (DutyScheduleInfoDto dutyScheduleInfoDto : dutyScheduleInfoDtoList) {
                    //添加值班组名称
                    int groupid = dutyScheduleInfoDto.getGroupid();
                    if (groupIdAndNames.containsKey(groupid))
                        dutyScheduleInfoDto.setGroupname(groupIdAndNames.get(groupid));

                    List<DutyScheduleShiftMemberDto> collect = dutyScheduleShiftMemberDtos.stream()
                            .filter(dutyScheduleShiftMemberDto -> dutyScheduleShiftMemberDto.getId() == dutyScheduleInfoDto.getId())
                            .sorted(Comparator.comparing(DutyScheduleShiftMemberDto::getOdr))
                            .collect(Collectors.toList());
                    dutyScheduleInfoDto.setMembers(collect);
                }
            }

            //遍历分组，添加分组的值班成员信息
            if (dutyScheduleShiftMemberDtos != null && !dutyScheduleShiftMemberDtos.isEmpty()) {
                for (DutyScheduleShiftMemberDto dto : dutyScheduleShiftMemberDtos) {
                    for (DutyStaffDto dutyStaffDto : allDutyStaffs) {
                        if (dto.getMemberid() == dutyStaffDto.getId()) {
                            dto.setMember(dutyStaffDto);
                        }
                    }
                }
            }

            return dutyScheduleInfoDtoList;
        }
        return null;
    }

    /**
     * 批量设置班次成员
     *
     * @param condition:设置信息
     * @return String 成功返回null。失败返回错误信息
     * @author QcCheng Tech
     * @date 2023/8/14
     */
    public String batchSetShiftMembers(DutyScheduleBatchSetMembersDto condition) throws QCPromptException {
        // 检查设置对象是否为空
        if (condition == null) {
            return QCUnifyReturnValue.Warn("设置对象不能为空");
        }

        // 检查值班分组ID集合是否为空
        if (condition.getGroupid() < 0) {
            return QCUnifyReturnValue.Warn("值班分组ID不能为空");
        }

        // 检查值班开始时间是否为空
        if (StringUtils.isBlank(condition.getBegintm())) {
            return QCUnifyReturnValue.Warn("开始时间不能为空");
        }

        // 检查值班结束时间是否为空
        if (StringUtils.isBlank(condition.getEndtm())) {
            return QCUnifyReturnValue.Warn("结束时间不能为空");
        }

        // 检查间隔天数是否小于1
        if (condition.getTeamcount() < 1) {
            return QCUnifyReturnValue.Warn("班值总数不能为空");
        }

        if ((condition.getStaffids() == null || condition.getStaffids().length == 0) && condition.getTeamid() < 1) {
            return QCUnifyReturnValue.Warn("未指定班次成员");
        }

        Date beginTime;
        Date endTime;

        try {
            // 解析值班开始时间
            beginTime = DateUtil.parseDate(condition.getBegintm());
            //时间精确到日
            beginTime = DateUtil.parseDate(DateUtil.getDateString(beginTime));
        } catch (Exception e) {
            // 抛出自定义异常，开始时间转换错误
            throw new QCPromptException("开始时间转换错误");
        }

        try {
            // 解析值班结束时间
            endTime = DateUtil.parseDate(condition.getEndtm());
            //时间精确到日
            endTime = DateUtil.parseDate(DateUtil.getDateString(endTime));
        } catch (Exception e) {
            // 抛出自定义异常，截止时间转换错误
            throw new QCPromptException("截止时间转换错误");
        }

        int teamid = condition.getTeamid();
        List<Integer> memberIdList = new ArrayList<>();

        // 如果选择了班值就获取该班值中的所有成员
        if (teamid > 0) {
            List<DutyTeamMemberDto> dutyTeamMemberDtos = dutyTeamMemberService.get(teamid);
            memberIdList = dutyTeamMemberDtos.stream()
                    .map(DutyTeamMemberDto::getMemberid)
                    .collect(Collectors.toList());
        }

        int[] staffids = condition.getStaffids();
        List<Integer> staffIdList = (staffids != null) ? Arrays.stream(staffids).boxed().collect(Collectors.toList()) : new ArrayList<>();

        List<Integer> mergedList = new ArrayList<>(staffIdList);
        mergedList.addAll(memberIdList);

        // 去除重复人员
        List<Integer> distinctMergedList = mergedList.stream().distinct().collect(Collectors.toList());

        if (distinctMergedList == null || distinctMergedList.isEmpty())
            throw new QCPromptException("设置的值班人员为空");

        //查询已有班次信息，按照班次顺序进行设置，以开始时间为准，开始时间为全等
        //查询条件： gid=groupId && begintm>=conditionBeginTime && endtm<=conditionEndTime
        LambdaQueryWrapper<DutySchedule> wrapper = new LambdaQueryWrapper<>();
        //只需要返回班次ID，其他信息不查询
        wrapper.select(DutySchedule::getId);

        wrapper.eq(DutySchedule::getGid, condition.getGroupid());
        wrapper.ge(DutySchedule::getBegintm, beginTime);
        wrapper.le(DutySchedule::getEndtm, endTime);
        List<DutySchedule> existSchedules = dutyScheduleRepository.selectList(wrapper);
        if (existSchedules == null || existSchedules.isEmpty())
            throw new QCPromptException("不存在满足条件的班次，请先进行排班");

        int index = 0x0;
        while (index < existSchedules.size()) {
            int scheduleId = existSchedules.get(index).getId();

            //删除该班次人员防止重复添加人员
            dutyScheduleMemberService.deleteScheduleMembers(scheduleId);

            // 添加班次人员
            dutyScheduleMemberService.addScheduleMembers(distinctMergedList, scheduleId);

            index += condition.getTeamcount();
        }

        return QCUnifyReturnValue.Success();
    }
}
